﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "Widget/SInputNumber.h"
#include "SlateOptMacros.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Input/SEditableText.h"

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION

void SInputNumber::Construct(const FArguments& InArgs)
{
	// OuterBox
	// 	HorizontalLayout
	// 		LeftButtonBox
	// 			LeftButton
	// 		InputTextBox
	// 			InputText
	// 		RightButtonBox
	// 			RightButton
	Text = InArgs._Text;
	Font = InArgs._Font;
	StepStrictly = InArgs._StepStrictly.Get();
	IsReadOnly = InArgs._IsReadOnly.Get();
	MinValue = InArgs._MinValue;
	MaxValue = InArgs._MaxValue;
	LeftButtonWidth = InArgs._LeftButtonWidth;
	RightButtonWidth = InArgs._RightButtonWidth;
	LeftButtonStyle = InArgs._LeftButtonStyle;
	RightButtonStyle = InArgs._RightButtonStyle;
	OnNumberTextException = InArgs._OnNumberTextException;
	OnNumberTextChanged = InArgs._OnNumberTextChanged;
	OnLeftButtonClicked = InArgs._OnLeftButtonClicked;
	OnRightButtonClicked = InArgs._OnRightButtonClicked;
	NumberStep = InArgs._NumberStep.Get();

	ChildSlot
	[
		SAssignNew(OuterBox, SBox)
		.HAlign(EHorizontalAlignment::HAlign_Fill)
		.VAlign(EVerticalAlignment::VAlign_Fill)
    	[
    	    SAssignNew(HorizontalLayout,SHorizontalBox)
    	    
    	    +SHorizontalBox::Slot()
    	    .HAlign(EHorizontalAlignment::HAlign_Fill)
    	    .VAlign(EVerticalAlignment::VAlign_Fill)
    		.Padding( FMargin( 0, 0, 0, 0 ) )
    		.AutoWidth()
    		[
    		    SAssignNew(LeftButtonBox,SBox)
    		    .HAlign(EHorizontalAlignment::HAlign_Fill)
				.VAlign(EVerticalAlignment::VAlign_Fill)
				.WidthOverride(LeftButtonWidth)
    		    [
    				SAssignNew(LeftButton,SButton)
    				.ButtonStyle(LeftButtonStyle)
    				.OnClicked(OnLeftButtonClicked)
    		    ]
    		]
    		
    		+SHorizontalBox::Slot()
    		.HAlign(EHorizontalAlignment::HAlign_Fill)
            .VAlign(EVerticalAlignment::VAlign_Fill)
            .Padding( FMargin( 0, 0, 0, 0 ) )
            .FillWidth(1.0f)
            [
                SAssignNew(InputTextBox,SBox)
                .HAlign(EHorizontalAlignment::HAlign_Center)
				.VAlign(EVerticalAlignment::VAlign_Center)
                [
                    SAssignNew(InputText,SEditableText)
                    .Text(Text)
                    .Font(Font)
                    .IsReadOnly(IsReadOnly)
                    .OnTextChanged(this, &SInputNumber::OnNumberTextChangedEvent)
                    .OnTextCommitted(this, &SInputNumber::OnNumberTextCommitted)
                ]
            ]
            
            +SHorizontalBox::Slot()
            .HAlign(EHorizontalAlignment::HAlign_Fill)
            .VAlign(EVerticalAlignment::VAlign_Fill)
            .Padding( FMargin( 0, 0, 0, 0 ) )
            .AutoWidth()
            [
                SAssignNew(RightButtonBox,SBox)
                .HAlign(EHorizontalAlignment::HAlign_Fill)
				.VAlign(EVerticalAlignment::VAlign_Fill)
				.WidthOverride(RightButtonWidth)
                [
                    SAssignNew(RightButton,SButton)
                    .ButtonStyle(RightButtonStyle)
                    .OnClicked(OnRightButtonClicked)
                ]
            ]
    	]
	];
	OnNumberTextCommitted(Text.Get(),ETextCommit::Default);	
}

END_SLATE_FUNCTION_BUILD_OPTIMIZATION

bool SInputNumber::SetNumberText(const TAttribute<FText>& NewText)
{
	if(ValideNumber(NewText.Get()))
	{
		if(ValideMaxNumber(NewText.Get().ToString())&&ValideMinNumber(NewText.Get().ToString()))
		{
			Text = NewText;
			InputText->SetText(Text);
			OnNumberTextChanged.ExecuteIfBound(Text.Get());
			return true;
		}
	}
	return false;
}

FText SInputNumber::GetNumberText()
{
	return InputText->GetText();
}

bool SInputNumber::SetNumberStep(const float value)
{
	NumberStep = value;
	return true;
}

bool SInputNumber::SetMinValue(const TAttribute<FString>& NewMinValue)
{
	if(ValideNumber(NewMinValue.Get()))
	{
		MinValue = NewMinValue;
		return true;
	}
	return false;
}

FString SInputNumber::GetMinValue()
{
	return MinValue.Get();
}

bool SInputNumber::SetMaxValue(const TAttribute<FString>& NewMaxValue)
{
	if(ValideNumber(NewMaxValue.Get()))
	{
		MaxValue = NewMaxValue;
		return true;
	}
	return false;
}

FString SInputNumber::GetMaxValue()
{
	return MaxValue.Get();
}

bool SInputNumber::SetLeftButtonWidth(const TAttribute<FOptionalSize>& NewLeftButtonWidth)
{
	LeftButtonWidth = NewLeftButtonWidth;
	LeftButtonBox->SetWidthOverride(LeftButtonWidth);
	return true;
}

FOptionalSize SInputNumber::GeLeftButtonWidth()
{
	return LeftButtonWidth.Get();
}

bool SInputNumber::SetRightButtonWidth(const TAttribute<FOptionalSize>& NewRightButtonWidth)
{
	RightButtonWidth = NewRightButtonWidth;
	RightButtonBox->SetWidthOverride(RightButtonWidth);
	return true;
}

FOptionalSize SInputNumber::GetRightButtonWidth()
{
	return RightButtonWidth.Get();
}

bool SInputNumber::SetLeftButtonStyle(const FButtonStyle* Value)
{
	LeftButton->SetButtonStyle(Value);
	return true;
}

bool SInputNumber::SetRightButtonStyle(const FButtonStyle* Value)
{
	RightButton->SetButtonStyle(Value);
	return true;
}

void SInputNumber::SetNumberFont(const TAttribute<FSlateFontInfo>& NewFont)
{
	Font = NewFont;
	InputText->SetFont(Font);
}

void SInputNumber::SetOnLeftButtonClicked(FOnClicked InOnClicked)
{
	OnLeftButtonClicked = InOnClicked;
	LeftButton->SetOnClicked(InOnClicked);
}

void SInputNumber::SetOnRightButtonClicked(FOnClicked InOnClicked)
{
	OnRightButtonClicked = InOnClicked;
	RightButton->SetOnClicked(InOnClicked);
}

bool SInputNumber::ValideNumber(const FText& Content)
{
	return ValideNumber(Content.ToString());
}

bool SInputNumber::ValideNumber(const FString& Content)
{
	if( Content.IsNumeric())
	{
		return true;
	}else
	{
		OnNumberTextException.ExecuteIfBound(FText::FromString(Content),EInputNumberException::NotNumber);
		return false;
	}
}

bool SInputNumber::ValideMaxNumber(const FString& Content)
{
	if(!ValideNumber(Content)) return false;
	float Temp = StringToFloat(Content);
	if(MaxValue.Get().Len() > 0 && ValideNumber(MaxValue.Get()))
	{
		float maxvalue = FCString::Atof(*MaxValue.Get());
		if(Temp <= maxvalue)
		{
			return true;
		} else
		{
			OnNumberTextException.ExecuteIfBound(FText::FromString(Content),EInputNumberException::MaxThenMaxValue);
			return false;
		}
		
	}
	return true;
}

bool SInputNumber::ValideMinNumber(const FString& Content)
{
	if(!ValideNumber(Content)) return false;
	float Temp=StringToFloat(Content);
	if(MinValue.Get().Len() > 0 && ValideNumber(MinValue.Get()))
	{
		float maxvalue = FCString::Atof(*MinValue.Get());
		if(Temp >= maxvalue)
		{
			return true;
		}else
		{
			OnNumberTextException.ExecuteIfBound(FText::FromString(Content),EInputNumberException::MinTnenMinValue);
			return false;
		}
	}
	return true;
}

float SInputNumber::StringToFloat(FString Content)
{
	if(Content.Len() > 0 && ValideNumber(Content))
	{
		return FCString::Atof(*Content);
	}
	return 0.0;
}

float SInputNumber::TextToFloat(FText Content)
{
	return StringToFloat(Content.ToString());
}

void SInputNumber::OnNumberTextChangedEvent(const FText& InText)
{
	if(!ValideNumber(InText))
	{
		SetNumberText(Text);
	}
	
}

void SInputNumber::OnNumberTextCommitted(const FText& CommentText, ETextCommit::Type CommitInfo)
{
	float tempfloat = TextToFloat(CommentText);
	if(StepStrictly)
	{
		float multiple = tempfloat/NumberStep;
		float decimal = FMath::Frac(multiple);
		float integer = multiple-decimal;
		if(multiple!=0.0)
		{
			tempfloat = NumberStep*(integer+1);
		}
	}
	FString temptext = FString::SanitizeFloat(tempfloat);
	SetNumberText(FText::FromString(temptext));
	if(!ValideMaxNumber(temptext))
		SetNumberText(FText::FromString(MaxValue.Get()));
	else if(!ValideMinNumber(temptext))
		SetNumberText(FText::FromString(MinValue.Get()));
	
}
